package environment;

import java.util.ArrayList;
import java.util.List;

public class VirtualMachine {

	private Integer id;
	private Integer loadCPUs;
	/* max. load of all running apps */
	private Integer maxloadCPUs;
	private Integer maxCPUs;
	private Integer reservedCPUs;
	
	private PhysicalMachine physicalMachine;
	private List<Application> applications = new ArrayList<Application>();
	
	public VirtualMachine(Integer id, Integer maxCPUs) {
		this.id = id;
		this.loadCPUs = 0;
		this.maxloadCPUs = 0;
		this.maxCPUs = maxCPUs;
		this.reservedCPUs = 0;
	}
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	/* 2 threads, probably not necessary
	 * scheduler: reads load
	 * monitor: adapts load 
	 */
	public Integer getLoadCPUs() {
		synchronized(loadCPUs) {
			return loadCPUs;
		}
	}
	public void addLoadCPUs(Integer loadCPUs) {
		synchronized(this.loadCPUs) {
			this.loadCPUs += loadCPUs;
		}
	}
	public void removeLoadCPUs(Integer loadCPUs) {
		synchronized(this.loadCPUs) {
			this.loadCPUs -= loadCPUs;
		}
	}
	
	public void removeMaxloadCPUs(Integer maxloadCPUs) {
		synchronized(this.maxloadCPUs) {
			this.maxloadCPUs-=maxloadCPUs;
		}
	}
	public void addMaxloadCPUs(Integer maxloadCPUs) {
		synchronized(this.maxloadCPUs) {
			this.maxloadCPUs+=maxloadCPUs;
		}
	}
	public Integer getMaxloadCPUs() {
		synchronized(maxloadCPUs) {
			return maxloadCPUs;
		}
	}

	public void removeReservedCPUs(Integer reservedCPUs) {
		synchronized(this.reservedCPUs) {
			this.reservedCPUs-=reservedCPUs;
		}
	}
	public void addReservedCPUs(Integer reservedCPUs) {
		synchronized(this.reservedCPUs) {
			this.reservedCPUs+=reservedCPUs;
		}
	}
	public Integer getReservedCPUs() {
		synchronized(reservedCPUs) {
			return reservedCPUs;
		}
	}
	
	public Integer getMaxCPUs() {
		return maxCPUs;
	}
	
	public void removeMaxCPUs(Integer maxCPUs) {
		synchronized(this.maxCPUs) {
			this.maxCPUs-=maxCPUs;
		}
		if(physicalMachine != null)
			physicalMachine.removeLoadCPUs(maxCPUs);
	}
	public void addMaxCPUs(Integer maxCPUs) {
		synchronized(this.maxCPUs) {
			this.maxCPUs+=maxCPUs;
		}
		if(physicalMachine != null)
			physicalMachine.addLoadCPUs(maxCPUs);
	}	

	public PhysicalMachine getPhysicalMachine() {
		return physicalMachine;
	}
	public List<Application> getApplications() {
		return applications;
	}	
	
	public void setPhysicalMachine(PhysicalMachine physicalMachine) {
		
			if(this.physicalMachine != null)
				synchronized(this.physicalMachine.getVirtualMachines()) {
					this.physicalMachine.getVirtualMachines().remove(this);
					this.physicalMachine.removeLoadCPUs(getMaxCPUs());
				}
			
			this.physicalMachine = physicalMachine;
			
			if(physicalMachine != null &&
					!physicalMachine.getVirtualMachines().contains(this))
				physicalMachine.addVirtualMachine(this);

	}
	
	public void addApplication(Application app)
	{
		if(app != null)
		{
			synchronized(applications) {
				applications.add(app);
			}
			
			if(app.getVirtualMachine() != this)
				app.setVirtualMachine(this);
			
			if(app.isRunning())
			{
				// app was moved from one vm to another
				addLoadCPUs(app.getLoadCPUs());
				addMaxloadCPUs(app.getMaxCPUs());
			}
			else
			{
				// reserve cpu for the app
				addReservedCPUs(app.getMaxCPUs());
			}
			
		}
	}
	
	public void removeApplication(Application app)
	{
		if(app != null)
		{
			synchronized(applications) {
				applications.remove(app);
			}
			
			app.setVirtualMachine(null);
		}
	}
	
	public Integer getFreeCPUs()
	{
		return getMaxCPUs() - getMaxloadCPUs() - getReservedCPUs();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result
				+ ((physicalMachine == null) ? 0 : physicalMachine.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		VirtualMachine other = (VirtualMachine) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		if (physicalMachine == null) {
			if (other.physicalMachine != null)
				return false;
		} else if (!physicalMachine.equals(other.physicalMachine))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "VirtualMachine [id=" + id + ", loadCPUs=" + loadCPUs
				+ ", maxloadCPUs=" + maxloadCPUs + ", maxCPUs=" + maxCPUs
				+ ", reservedCPUs=" + reservedCPUs + "]";
	}	
}
